package com.ecloud.support.generator.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.ecloud.common.base.model.BasePo;
import com.ecloud.common.base.result.Result;
import com.ecloud.common.base.result.ResultBuilder;
import com.ecloud.common.base.result.ResultStatus;
import com.ecloud.common.utils.check.CollectionUtils;
import com.ecloud.common.utils.check.StringUtils;
import com.ecloud.common.utils.code.UUIDUtil;
import com.ecloud.common.utils.file.FileUtils;
import com.ecloud.common.utils.file.ZipUtils;
import com.ecloud.support.generator.custom.CustomerConstVal;
import com.ecloud.support.generator.custom.CustomerTemplateConfig;
import com.ecloud.support.generator.custom.CustomerTemplateEngine;
import com.ecloud.support.generator.custom.DefaultTemplateEngine;
import com.ecloud.support.generator.mapper.DbConfigMapper;
import com.ecloud.support.generator.mapper.TableConfigMapper;
import com.ecloud.support.generator.mapper.TemplateMapper;
import com.ecloud.support.generator.model.convert.TableConfigConverter;
import com.ecloud.support.generator.model.dto.TableConfigDto;
import com.ecloud.support.generator.model.entity.DbConfigPo;
import com.ecloud.support.generator.model.entity.TableConfigPo;
import com.ecloud.support.generator.model.entity.TemplatePo;
import com.ecloud.support.generator.model.vo.TableConfigVo;
import com.ecloud.support.generator.service.iface.ITableConfigService;

/**
 * TableConfigService
 *
 * @author LiuGangQiang Create in 2021/04/15
 */
@Service
public class TableConfigService extends ServiceImpl<TableConfigMapper, TableConfigPo> implements ITableConfigService {
    @Resource
    private DbConfigMapper dbConfigMapper;
    @Resource
    private TemplateMapper templateMapper;

    /**
     * @see com.ecloud.support.generator.service.iface.ITableConfigService#insert(com.ecloud.support.generator.model.dto.TableConfigDto)
     */
    @Override
    public Result<?> insert(TableConfigDto dto) {
        /* 判断数据库是否存在记录 */
        LambdaQueryWrapper<TableConfigPo> queryWrapper = new QueryWrapper<TableConfigPo>().lambda();
        queryWrapper.select(TableConfigPo::getId);
        queryWrapper.eq(TableConfigPo::getDbId, dto.getDbId());
        queryWrapper.eq(TableConfigPo::getTableName, dto.getTableName());
        TableConfigPo exists = this.getOne(queryWrapper);
        TableConfigPo entity = TableConfigConverter.INSTANCE.dtoToPo(dto);
        if (exists != null) {
            LambdaUpdateWrapper<TableConfigPo> updateWrapper = new UpdateWrapper<TableConfigPo>().lambda();
            updateWrapper.eq(TableConfigPo::getId, exists.getId());
            /* 存在则修改 */
            if (this.update(entity, updateWrapper)) {
                return ResultBuilder.build(ResultStatus.OK);
            }
            return ResultBuilder.build(ResultStatus.FAIL);
        }
        /* 不存在则新增 */
        entity.setId(UUIDUtil.uuid());
        if (this.save(entity)) {
            return ResultBuilder.build(ResultStatus.OK);
        }
        return ResultBuilder.build(ResultStatus.FAIL);
    }

    /**
     * @see com.ecloud.support.generator.service.iface.ITableConfigService#generator(com.ecloud.support.generator.model.dto.TableConfigDto)
     */
    @Override
    public ResponseEntity<byte[]> generator(TableConfigDto dto) throws IOException {
        List<String> tables = new ArrayList<>();
        tables.add(dto.getTableName());
        return generate(dto, tables);
    }

    /**
     * @see com.ecloud.support.generator.service.iface.ITableConfigService#generators(com.ecloud.support.generator.model.dto.TableConfigDto)
     */
    @Override
    public ResponseEntity<byte[]> generators(TableConfigDto dto) throws IOException {
        return generate(dto, dto.getTables());
    }

    /**
     * 代码生成
     *
     * @author LiuGangQiang Create in 2021/06/02
     * @param dto    入参
     * @param tables 表名
     * @return {@link ResponseEntity}
     * @throws IOException
     */
    private ResponseEntity<byte[]> generate(TableConfigDto dto, List<String> tables) throws IOException {
        /* 生成一个不重复的UUID作为文件目录方便后期打包下载 */
        String path = CustomerConstVal.PATH + UUIDUtil.generateShortUuid();
        Boolean isDefault = true;
        CustomerTemplateConfig config = new CustomerTemplateConfig();
        String customerPath = path + CustomerConstVal.SEPARATOR + dto.getPackageName().replace(CustomerConstVal.PKG_SPLIT_REPLACE, CustomerConstVal.SEPARATOR)
                + CustomerConstVal.SEPARATOR;
        config.setDtoPath(customerPath + dto.getDtoPackage().replace(CustomerConstVal.PKG_SPLIT_REPLACE, CustomerConstVal.SEPARATOR));
        config.setVoPath(customerPath + dto.getVoPackage().replace(CustomerConstVal.PKG_SPLIT_REPLACE, CustomerConstVal.SEPARATOR));
        config.setConvertPath(customerPath + dto.getConvertPackage().replace(CustomerConstVal.PKG_SPLIT_REPLACE, CustomerConstVal.SEPARATOR));
        /* 获取对应数据库连接信息 */
        DbConfigPo dbConfig = dbConfigMapper.selectById(dto.getDbId());
        /* 实例化代码生成器 */
        AutoGenerator generator = new AutoGenerator(new DataSourceConfig.Builder(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword()).build());

        /* 全局策略配置 */
        GlobalConfig gc = new GlobalConfig.Builder().outputDir(path)
                .fileOverride()
                .openDir(false)
                .author(dto.getAuthor())
                .dateType(DateType.TIME_PACK)
                .commentDate(CustomerConstVal.DATE_FORMAT)
                .build();
        generator.global(gc);

        /* 生成策略配置 */
        StrategyConfig sc = new StrategyConfig.Builder().addTablePrefix(dto.getPrefix())
                .addInclude(tables.toArray(new String[tables.size()]))
                .enableSkipView()
                /* 实体类参数 */
                .entityBuilder()
                .superClass(BasePo.class)
                .enableSerialVersionUID()
                .enableLombok()
                .enableTableFieldAnnotation()
                .enableRemoveIsPrefix()
                .naming(NamingStrategy.underline_to_camel)
                .columnNaming(NamingStrategy.underline_to_camel)
                .formatFileName(dto.getEntityName())
                /* 控制器参数 */
                .controllerBuilder()
                .enableRestStyle()
                .enableHyphenStyle()
                .formatFileName(dto.getControllerName())
                /* 服务类参数 */
                .serviceBuilder()
                .formatServiceFileName(dto.getServiceName())
                .formatServiceImplFileName(dto.getServiceImplName())
                /* Mapper参数 */
                .mapperBuilder()
                .formatMapperFileName(dto.getMapperName())
                .formatXmlFileName(dto.getXmlName())
                .enableBaseResultMap()
                .build();
        generator.strategy(sc);

        /* 包策略配置 */
        PackageConfig pc = new PackageConfig.Builder().parent(dto.getPackageName())
                .moduleName(dto.getModelName())
                .entity(dto.getEntityPackage())
                .service(dto.getServicePackage())
                .serviceImpl(dto.getServiceImplPackage())
                .mapper(dto.getMapperPackage())
                .xml(dto.getMapperPackage())
                .controller(dto.getControllerPackage())
                .build();
        generator.packageInfo(pc);

        /* 判断是否使用默认模板 */
        if (StringUtils.isNotBlank(dto.getTemplateSetId())) {
            LambdaQueryWrapper<TemplatePo> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(TemplatePo::getTemplateName, TemplatePo::getContent);
            wrapper.eq(TemplatePo::getTemplateSetId, dto.getTemplateSetId());
            List<TemplatePo> templates = templateMapper.selectList(wrapper);
            /* 判断自定义模板是否存在 */
            if (CollectionUtils.isNotEmpty(templates)) {
                isDefault = false;
                /* 使用自定义配置模板并默认禁用所有 */
                config.disable();
                /* 存在则生成临时模板并使用自定义模板 */
                for (TemplatePo template : templates) {
                    String fpath = path + CustomerConstVal.SEPARATOR + template.getTemplateName();
                    /* 判断文件是否存在 存在则删除防止缓存 */
                    if (FileUtils.exist(fpath)) {
                        FileUtils.delete(fpath);
                    }
                    /* 创建新文件 并写入内容到新文件中采用覆盖模式 */
                    FileUtils.write(template.getContent(), FileUtils.create(fpath), false);
                    switch (template.getTemplateName()) {
                    case CustomerConstVal.CONTROLLER_JAVA_VM:
                        config.setController(fpath);
                        break;
                    case CustomerConstVal.ENTITY_JAVA_VM:
                        config.setEntity(fpath);
                        break;
                    case CustomerConstVal.MAPPER_JAVA_VM:
                        config.setMapper(fpath);
                        break;
                    case CustomerConstVal.MAPPER_XML_VM:
                        config.setXml(fpath);
                        break;
                    case CustomerConstVal.SERVICE_JAVA_VM:
                        config.setService(fpath);
                        break;
                    case CustomerConstVal.SERVICEIMPL_JAVA_VM:
                        config.setServiceImpl(fpath);
                        break;
                    case CustomerConstVal.DTO_JAVA_VM:
                        config.setDto(fpath);
                        break;
                    case CustomerConstVal.VO_JAVA_VM:
                        config.setVo(fpath);
                        break;
                    case CustomerConstVal.CONVERT_JAVA_VM:
                        config.setConvert(fpath);
                        break;
                    default:
                    }
                }
                generator.template(new TemplateConfig.Builder().controller(config.getController())
                        .mapper(config.getMapper())
                        .entity(config.getEntity())
                        .mapperXml(config.getXml())
                        .service(config.getService(), config.getServiceImpl())
                        .build());
            }
        }
        /* 执行生成 */
        generator.execute(isDefault ? new DefaultTemplateEngine(dto, config) : new CustomerTemplateEngine(dto, config));
        /* 打包下载 */
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        String root = dto.getPackageName().split(CustomerConstVal.PKG_SPLIT)[0];
        ZipUtils.toZip(path + CustomerConstVal.SEPARATOR + root, baos);
        /* 删除文件目录 */
        FileUtils.delete(path);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentDispositionFormData(CustomerConstVal.HEADER_ATTACHMENT,
                URLEncoder.encode(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + CustomerConstVal.OUT_FILE_SUFFIX, CustomerConstVal.CHARSET_UTF8));
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentLength(baos.size());
        headers.setConnection(CustomerConstVal.HTTP_CONNECTION_CLOSE);
        return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.OK);
    }

    /**
     * @see com.ecloud.support.generator.service.iface.ITableConfigService#queryDetail(com.ecloud.support.generator.model.dto.TableConfigDto)
     */
    @Override
    public Result<?> queryDetail(TableConfigDto dto) {
        LambdaQueryWrapper<TableConfigPo> queryWrapper = new QueryWrapper<TableConfigPo>().lambda();
        queryWrapper.eq(TableConfigPo::getDbId, dto.getDbId());
        queryWrapper.eq(TableConfigPo::getTableName, dto.getTableName());
        TableConfigPo po = this.baseMapper.selectOne(queryWrapper);
        if (po != null) {
            TableConfigVo vo = TableConfigConverter.INSTANCE.poToVo(po);
            return ResultBuilder.build(ResultStatus.OK, vo);
        }
        return ResultBuilder.build(ResultStatus.DATA_NOT_FOUND);
    }
}
